require 'rexml/document'
require 'rexml/xpath'
require 'fileutils'

# category +---+< api_accessor +---+< occasion_item
class OccasionItem
   # 必須項目, 他はAPIに応じて動的に定義
   attr_accessor :url, :name, :thumb, :copy, :selected
   attr_writer :rank

   # ならび順に使う重み
   def rank
     @rank + rand(50)/10.0
   end

   # 商品名と商品販売元が同じ
   def same?( item )
      return false if( @name != item.name || @maker != item.maker )
      return false if( @name.nil?  || @maker.nil? )
      return false if( @name == "" || @maker == "" )
      true
   end

   # 完全に同じ
   def equals?( item )
      return false if( self.class == item.class )
   end

   def <=> other
     other.rank - self.rank
   end

   class << self
       def find options, selected_urls = []
           category = options[:category]
           keyword = options[:keyword]  # notyet

           occasion_items = []
           api_accessors = read_occasion_setting category
           api_accessors.each do |api_accessor|
               xml = request_to api_accessor["query"]
               # sort用に使う重み付けがapiごとにoccasion_setting.ymlに定義されている
               # # xpath でattrごとに配列を作成して, それを
               base_rank = api_accessor["rank"]
               occasion_items += self.new_from_xml(xml, api_accessor["elements"], base_rank).to_a
           end

           # 追加済みのitemはselectedをtrueにする
           occasion_items.collect do |item|
             item.selected = true if selected_urls.include?(item.url)
           end

           return occasion_items.sort
       end
 
       def request_to url
           $stderr.puts "GET: #{url}" if $debug_off_line
           url = URI.escape(url)

           # キャッシュ機構
           cache_path = "./tmp/cache/" + url.sub(%r{^.*://}, "./") # fixme use rails_cache 
           FileUtils.makedirs(File.dirname(cache_path))
           cache_path_life_time = File.file?(cache_path) ? Time.now - File.ctime(cache_path) : 9999999
           xml = ""
           if cache_path_life_time < 60*60 or $debug_off_line
              ActiveRecord::Base.logger.info "  Cache Access: #{url} [life time: #{cache_path_life_time}]" # fixme logger
              File.open(cache_path, "r"){|f| xml = f.read }
           else
              ActiveRecord::Base.logger.info "  API Access: #{url}"
              begin
                xml = open(url){|u| u.read } 
                File.open(cache_path, "w"){|w| w.puts xml }
              rescue
                ActiveRecord::Base.logger.warn "  API Access Miss: #{url}"
                xml = File.open(cache_path, "r"){|f| f.read }
              end
           end
           return xml
       end

       # xml -> OccasionItem
       def new_from_xml xml, elements, rank=1 # fixme complecated!
           class_eval "attr_accessor #{elements.keys.collect{|k|':'+k}.join(', ')}"
           rexml = REXML::Document.new(xml)
           items = []
           elements.each do |attr, xpath|
               list = REXML::XPath.match(rexml,xpath)
               (0..(list.size-1)).each do |idx|
                   items[idx] = self.new unless items[idx]
                   eval "items[idx].#{attr} = list[idx].text"
                   items[idx].rank = rank
               end
           end
           return items
        end

        # occasion_setting読み込み, apiへの接続定義を生成 
        def read_occasion_setting category, file="config/occasion_setting.yml" # fixme hard cording
            str = open(file){|f| f.read }
            data = YAML.load(str)
            api_accessors = data[category.to_s]
            begin
              api_accessors.collect do |accessor|
                common_setting = data["COMMON"][accessor["api"]]
                accessor["query"] = common_setting["url"] + accessor["query"]
                accessor["elements"] = common_setting["elements"]
              end
            rescue 
              # fixme .. need me ?
            end
            return api_accessors
        end
    end
end



if __FILE__ == $0
    $debug_off_line = true
    occasion_items = OccasionItem.find(:category => :Baby)

    p occasion_items.first.name
end
